package com.prawn.authority.service;

import java.util.*;
import java.util.stream.Stream;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import com.prawn.authority.dao.RoleDao;
import com.prawn.authority.dao.UserDao;
import com.prawn.authority.pojo.FunctionParent;
import com.prawn.authority.pojo.Role;
import com.prawn.authority.pojo.User;
import com.prawn.authority.utils.GlobalException;
import com.prawn.authority.utils.SecurityContextUtil;
import entity.Result;
import entity.StatusCode;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import com.prawn.authority.dao.FunctionDao;
import com.prawn.authority.pojo.Function;

/**
 * 服务层
 *
 * @author Administrator
 */
@Service
public class FunctionService {

	@Autowired
	private RoleDao roleDao;

	@Autowired
	private RoleService roleService;

	@Autowired
	private FunctionDao functionDao;

	@Autowired
	private UserDao userDao;

	/**
	 * 查找未对该角色授权的权限
	 *
	 * @param roleId
	 * @return
	 */
	public List<Function> findOthersByRoleId(String roleId) {
		// 查询角色id是否存在
		roleService.findById(roleId);
		return functionDao.findOthersByRoleId(roleId);
	}


	/**
	 * 查询全部列表
	 *
	 * @return
	 */
	public List<Function> findAll() {
		return functionDao.findAll();
	}


	/**
	 * 条件查询+分页
	 *
	 * @param whereMap
	 * @param page
	 * @param size
	 * @return
	 */
	public Page<Function> findSearch(Map whereMap, int page, int size) {
		Specification<Function> specification = createSpecification(whereMap);
		PageRequest pageRequest = PageRequest.of(page - 1, size);
		return functionDao.findAll(specification, pageRequest);
	}


	/**
	 * 条件查询
	 *
	 * @param whereMap
	 * @return
	 */
	public List<Function> findSearch(Map whereMap) {
		Specification<Function> specification = createSpecification(whereMap);
		return functionDao.findAll(specification);
	}

	/**
	 * 根据ID查询实体
	 *
	 * @param id
	 * @return
	 */
	public Function findById(String id) {
		Optional<Function> function = functionDao.findById(id);
		// 若为空就抛出错误
		if (!function.isPresent()) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "权限不存在"));
		}
		return function.get();
	}

	/**
	 * 动态条件构建
	 *
	 * @param searchMap
	 * @return
	 */
	private Specification<Function> createSpecification(Map searchMap) {

		return new Specification<Function>() {

			@Override
			public Predicate toPredicate(Root<Function> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> predicateList = new ArrayList<Predicate>();
				// id
				if (searchMap.get("id") != null && !"".equals(searchMap.get("id"))) {
					predicateList.add(cb.like(root.get("id").as(String.class), "%" + (String) searchMap.get("id") + "%"));
				}
				// 名称
				if (searchMap.get("name") != null && !"".equals(searchMap.get("name"))) {
					predicateList.add(cb.like(root.get("name").as(String.class), "%" + (String) searchMap.get("name") + "%"));
				}
				// 父级编号
				if (searchMap.get("parentId") != null && !"".equals(searchMap.get("parentId"))) {
					predicateList.add(cb.like(root.get("parentId").as(String.class), "%" + (String) searchMap.get("parentId") + "%"));
				}
				// 排序
				if (searchMap.get("sort") != null && !"".equals(searchMap.get("sort"))) {
					predicateList.add(cb.equal(root.get("sort"), searchMap.get("sort")));
				}
				// 链接
				if (searchMap.get("href") != null && !"".equals(searchMap.get("href"))) {
					predicateList.add(cb.like(root.get("href").as(String.class), "%" + (String) searchMap.get("href") + "%"));
				}

				// 备注信息
				if (searchMap.get("remarks") != null && !"".equals(searchMap.get("remarks"))) {
					predicateList.add(cb.like(root.get("remarks").as(String.class), "%" + (String) searchMap.get("remarks") + "%"));
				}

				return cb.and(predicateList.toArray(new Predicate[predicateList.size()]));

			}
		};

	}


	/**
	 * 更新角色权限
	 *
	 * @param roleId
	 * @param functionIdList
	 */
	@Transactional
	@Async
	public void UpdateFunOfRole(String roleId, Map functionIdList) {
		try {
			// 查询角色
			Role role = roleDao.findRoleById(roleId);
			if (role == null) {
				throw new GlobalException(new Result(false, StatusCode.ERROR, "角色不存在"));
			}

//			// 删除角色的所有权限
//			roleDao.deleteALLById(roleId);

			List<Object> functionList = (List<Object>) functionIdList.get("functionIdList");
			if (functionIdList == null || functionIdList.size() == 0){
				roleDao.deleteALLById(roleId);
				return;
			}
			Set<Object> modifiedSet = new HashSet<>(functionList);
			Set<Object> originSet = new HashSet<>(functionDao.findFunctionIdByRoleId(roleId));
			if (originSet.isEmpty()){
				for (Object functionId : functionList){
					Function function = functionDao.findFunctionById((String) functionId);
					if (function == null && functionId != null) {
						throw new GlobalException(new Result(false, StatusCode.ERROR, functionId + "权限不存在"));
					}
					functionDao.addRoleFunction(roleId, (String) functionId);
				}
				return;
			}
			for (Object functionId : modifiedSet){
				if (!originSet.contains(functionId)){
					// 查询权限
					Function function = functionDao.findFunctionById((String) functionId);
					if (function == null && functionId != null) {
						throw new GlobalException(new Result(false, StatusCode.ERROR, functionId + "权限不存在"));
					}
					// 添加权限
					functionDao.addRoleFunction(roleId, (String) functionId);
				}
			}
			for (Object functionId : originSet){
				if (!modifiedSet.contains(functionId)){
					functionDao.deleteFunctionByRoleId((String) functionId,roleId);
				}
			}
		} catch (Exception e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "更新角色权限失败," + e.getMessage()));
		}
	}

	public List<Function> findByRoleId(String roleId) {
		// 查询角色id是否存在
		roleService.findById(roleId);
		return functionDao.findByRoleId(roleId);
	}

	/**
	 * 查询用户的所有权限
	 *
	 * @param userId
	 * @return
	 */
	public List<Function> findByUserId(String userId) {
		List<Function> functionList = new ArrayList<>();
		List<Role> roles = roleDao.findByUserId(userId);
		for (Role role : roles) {
			functionList.addAll(functionDao.findByRoleId(role.getId()));
		}

		return functionList;
	}

	/**
	 * 权限分级
	 *
	 * @param functionList
	 * @return
	 */

	public List<FunctionParent> grand(List<Function> functionList) {

		// 保存一级权限的集合
		List<FunctionParent> oneLevelFunctionList = new ArrayList<>();

		// 添加一级权限
		for (Function function : functionList) {
			// pid为空的即为第一级权限
			if (function.getParentId() == null) {
				// 创建一级权限节点
				FunctionParent oneLevelFunction = new FunctionParent();
				oneLevelFunction.setChildren(new ArrayList<FunctionParent>());
				// 把当前遍历到的权限的信息赋值给一级权限节点
				BeanUtils.copyProperties(function, oneLevelFunction);
				// 一级权限集合添加该一级权限节点
				oneLevelFunctionList.add(oneLevelFunction);
			}
		}

		// 添加二级权限
		for (Function twoFunction : functionList) {
			for (FunctionParent oneLevelFunction : oneLevelFunctionList) {
				// 一级权限的节点的Id等于二级权限节点的pid
				if (oneLevelFunction.getId().equals(twoFunction.getParentId())) {
					// 创建二级权限节点
					FunctionParent twoLevelFunction = new FunctionParent();
					twoLevelFunction.setChildren(new ArrayList<FunctionParent>());
					// 将当前遍历到的权限赋值给二级权限节点
					BeanUtils.copyProperties(twoFunction, twoLevelFunction);
					// 将该二级权限节点放入到对应的一级权限节点的children集合中
					oneLevelFunction.getChildren().add(twoLevelFunction);
				}
			}
		}

		// 添加三级权限
		for (Function threeFunction : functionList) {
			for (FunctionParent oneLevelFunction : oneLevelFunctionList) {
				List<FunctionParent> twoLevelFunctionList = oneLevelFunction.getChildren();
				for (FunctionParent twoLevelFunction : twoLevelFunctionList) {
					if (twoLevelFunction.getId().equals(threeFunction.getParentId())) {
						FunctionParent threeLevelFunction = new FunctionParent();
						BeanUtils.copyProperties(threeFunction, threeLevelFunction);
						twoLevelFunction.getChildren().add(threeLevelFunction);
					}
				}
			}
		}
		return oneLevelFunctionList;
	}

	/**
	 * 老板更新基地角色权限
	 * @param baseId
	 * @param baseIdentity
	 * @param functionIdList
	 */
	@Transactional
	public void UpdateBaseFunOfRole(String baseId,Integer baseIdentity, Map functionIdList) {
		try {
			User user = userDao.findByUserId(SecurityContextUtil.getUserDetails().getUserId());
			//查询执行操作的用户是否为基地老板 且同时验证角色id是否存在且该角色是否在老板基地中。
			if (!user.getBaseId().equals(baseId) || user.getBaseIdentity() != 2){
				throw new GlobalException(new Result(false, StatusCode.ERROR, "该用户不是基地老板，无法执行操作"));
			}

			// 删除角色的所有权限
			roleDao.deleteALLBaseFunctionById(baseId,baseIdentity);

			List<Object> functionList = (List<Object>) functionIdList.get("functionIdList");
			for (Object functionId : functionList) {
				// 查询权限
				Function function = functionDao.findFunctionById((String) functionId);
				if (function == null && functionId != null) {
					throw new GlobalException(new Result(false, StatusCode.ERROR, functionId + "权限不存在"));
				}
				// 添加权限
				functionDao.addBaseRoleFunction(baseId,baseIdentity, (String) functionId);
			}
		} catch (Exception e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "更新角色权限失败," + e.getMessage()));
		}
	}

	public List<Function> findByBase(String baseId,Integer baseIdentity){
		User user = userDao.findByUserId(SecurityContextUtil.getUserDetails().getUserId());
		if (user == null){
			throw new GlobalException(new Result(false, StatusCode.ERROR, "用户不存在"));
		}
		//查询执行操作的用户是否为基地老板 且同时验证角色id是否存在且该角色是否在老板基地中。
		if (!user.getBaseId().equals(baseId) || (user.getBaseIdentity() != 2 && user.getBaseIdentity() != baseIdentity)){
			throw new GlobalException(new Result(false, StatusCode.ERROR, "该用户不是基地老板，无法执行操作"));
		}
		if (baseIdentity < 2){
			throw new GlobalException(new Result(false, StatusCode.ERROR, "无法对该用户进行操作"));
		}
		if (baseIdentity == 2){
			return findByUserId(user.getUserId());
		}
		return functionDao.findFunctionByBase(baseId,baseIdentity);
	}
}